package com.example.lottery_system.service.impl;

import cn.hutool.crypto.digest.DigestUtil;
import com.example.lottery_system.common.errcode.ServiceErrorCodeConstant;
import com.example.lottery_system.common.exception.ServiceException;
import com.example.lottery_system.common.utils.JWTUtil;
import com.example.lottery_system.common.utils.RegexUtil;
import com.example.lottery_system.dao.DataObject.EncryptDO;
import com.example.lottery_system.dao.DataObject.UserDO;
import com.example.lottery_system.dao.Mapper.UserMapper;
import com.example.lottery_system.service.Enum.IdentityEnum;
import com.example.lottery_system.service.UserService;
import com.example.lottery_system.service.VerificationCodeService;
import com.example.lottery_system.service.dto.UserListDto;
import com.example.lottery_system.service.dto.UserLoginDto;
import com.example.lottery_system.service.dto.UserRegisterDto;
import com.example.lottery_system.controller.param.ShortMessageParam;
import com.example.lottery_system.controller.param.UserLoginParam;
import com.example.lottery_system.controller.param.UserPasswordParam;
import com.example.lottery_system.controller.param.UserRegisterParam;
import com.example.lottery_system.controller.result.UserLoginResult;
import jakarta.annotation.Resource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Service
public class UserServiceImpl implements UserService {
    Logger logger = LoggerFactory.getLogger(UserServiceImpl.class);

    @Resource(name = "userMapper")
    UserMapper userMapper;

    @Autowired
    VerificationCodeService verificationCodeService;
    @Override
    public UserRegisterDto register(UserRegisterParam param) {

        /**
         1，校验注册参数
         */
        checkRegisterParam(param);

        /**
         2，获取加密后的密码和手机号
          */
        UserDO userDo = new UserDO();
        userDo.setEmail(param.getEmail());
        userDo.setIdentity(param.getIdentity());
        // 设置密码 
        java.lang.String securityCode = DigestUtil.sha256Hex(param.getPassword());
        userDo.setPassword(securityCode);
        userDo.setUserName(param.getUserName());
        // 注意类型的转化
        userDo.setPhoneNumber(new EncryptDO(param.getPhoneNumber()));


        /**
          3. 将数据保存在数据中
         */
        try {
           userMapper.insertUser(userDo);
        } catch (Exception e) {
            logger.error("插入用户数据异常!");
            throw new ServiceException(ServiceErrorCodeConstant.REGISTER_INSERT_ERROR);
        }

        /**
          4. 构造dto数据 并返回
         */
        UserRegisterDto userRegisterDto = new UserRegisterDto();
        userRegisterDto.setId(userDo.getId());
        return userRegisterDto;
    }


    @Override
    public UserLoginResult login(UserLoginParam param) {
        /**
         * 1. 分密码登录 和 验证码登录
          */
        UserLoginDto userLoginDto;
        if (param instanceof UserPasswordParam userPasswordParam) {
            // 用户密码登录
            userLoginDto =  userLoginByPassword(userPasswordParam);
        } else if (param instanceof ShortMessageParam shortMessageParam) {
//            用户验证码登录
            userLoginDto = userLoginByVerificationCode(shortMessageParam);
        } else {
            logger.error("校验密码还是验证码时发生了登录参数异常!");
            throw new ServiceException(ServiceErrorCodeConstant.LOGIN_PARAM_ERROR);
        }

        // 类型状换为结果
        UserLoginResult result = new UserLoginResult();
        result.setToken(userLoginDto.getToken());
        result.setIdentity(userLoginDto.getIdentity().name());
        return result;
    }

    @Override
    public List<UserListDto> getUserDtoList(String identity) {
//        身份信息验证
        if (identity != null && null == IdentityEnum.forName(identity)) {
            throw new ServiceException(ServiceErrorCodeConstant.REGISTER_IDENTITY_ERROR);
        }

        // 开始查询
        List<UserDO> userDOS =  userMapper.selectUserListByIdentity(identity);

      List<UserListDto> userListDtos =  userDOS.stream().map(userDO ->
      {
        UserListDto userListDto = new UserListDto();
        userListDto.setUserName(userDO.getUserName());
        userListDto.setIdentity(IdentityEnum.forName(userDO.getIdentity()));
        userListDto.setPhoneNumber(userDO.getPhoneNumber().getPhone());
        userListDto.setEmail(userDO.getEmail());
        userListDto.setId(userDO.getId());

        return userListDto;
      }).collect(Collectors.toList());

        return userListDtos;
    }

    @Override
    public Boolean deleteUser(Long userId) {
        if (null == userId) {
            throw new ServiceException(ServiceErrorCodeConstant.USER_DELETE_IS_EMPTY);
        }

        /**
         * 删除
         */
        try {
            userMapper.deleteUser(userId);
        } catch (Exception e) {
            throw new ServiceException(ServiceErrorCodeConstant.USER_DELETE_ERROR);
        }
        return null;
    }


    private UserLoginDto userLoginByVerificationCode(ShortMessageParam shortMessageParam) {



        /**
         * 1. 然后校验是手机号还是邮箱
         */
        String number = shortMessageParam.getNumber();
        UserDO userDO = null;
        if (RegexUtil.checkMobile(number)) {
            userDO = userMapper.userInfoByPhone(new EncryptDO(shortMessageParam.getNumber()));
            if (null == userDO) {
                throw new ServiceException(ServiceErrorCodeConstant.LOGIN_USERNAME_NOT_EXIST);
            }
            /**
             * 2. 然后校验身份
             */
            checkLoginIdentity(shortMessageParam,userDO);

            /**
             * 手机号走这边
             */
            if (!verificationCodeService.getVerificationCodeFromPhone(shortMessageParam.getNumber()).trim()
                    .equals(shortMessageParam.getCode().trim())) {
                logger.error("验证码处验证错误!");
                throw  new ServiceException(ServiceErrorCodeConstant.LOGIN_CODE_ERROR);
            }
        } else if (RegexUtil.checkMail(number)) {
            /**
             * 邮箱走这边
             */
            userDO = userMapper.userInfoByEmail(number);
            if (null == userDO) {
                throw new ServiceException(ServiceErrorCodeConstant.LOGIN_USERNAME_NOT_EXIST);
            }
            /**
             * 2. 然后校验身份
             */
            checkLoginIdentity(shortMessageParam,userDO);
            if (!verificationCodeService.getVerificationCodeFromEmail(shortMessageParam.getNumber()).trim()
                    .equals(shortMessageParam.getCode().trim())) {
                //  如果邮箱和短信都不相等我们才走到这里来
                logger.error("验证码处验证错误!");
                throw  new ServiceException(ServiceErrorCodeConstant.LOGIN_CODE_ERROR);
            }
        } else {
            logger.error("校验密码还是验证码时发生了登录参数异常!");
            throw new ServiceException(ServiceErrorCodeConstant.LOGIN_PARAM_ERROR);
        }

        /**
         * 3. 存入 token
         */
        UserLoginDto userLoginDto = checkSuccess(userDO);
        return userLoginDto;
    }

    private void checkLoginIdentity(ShortMessageParam shortMessageParam, UserDO userDO) {
        if (StringUtils.hasLength(shortMessageParam.getIdentity()) &&
                !shortMessageParam.getIdentity().equalsIgnoreCase(userDO.getIdentity())) {
            logger.error("验证码处身份信息异常!");
            throw new ServiceException(ServiceErrorCodeConstant.REGISTER_IDENTITY_ERROR);
        }
    }


    private UserLoginDto userLoginByPassword(UserPasswordParam userPasswordParam) {
        // 检查是用户名是手机号还是邮箱
        UserDO userDo = null;

        if (RegexUtil.checkMobile(userPasswordParam.getUserName())) {
            // 手机号
            userDo = userMapper.userInfoByPhone(new EncryptDO(userPasswordParam.getUserName()));
        } else if (RegexUtil.checkMail(userPasswordParam.getUserName())) {
            userDo = userMapper.userInfoByEmail(userPasswordParam.getUserName());
        } else {
            throw new ServiceException(ServiceErrorCodeConstant.LOGIN_USERNAME_ERROR);
        }

        // 2. 开始校验
        if (userDo == null) {
            throw new ServiceException(ServiceErrorCodeConstant.LOGIN_USERNAME_NOT_EXIST);
        } else if (StringUtils.hasLength(userPasswordParam.getIdentity()) &&
            !userDo.getIdentity().equalsIgnoreCase(userPasswordParam.getIdentity())) {
            throw new ServiceException(ServiceErrorCodeConstant.REGISTER_IDENTITY_ERROR);
        } else if (!userDo.getPassword().equals(DigestUtil.sha256Hex(userPasswordParam.getPassword()))) {
//            校验密码
                throw  new ServiceException(ServiceErrorCodeConstant.LOGIN_PASSWORD_ERROR);
        }

        // 3. 验证成功生成 token 返回
        UserLoginDto userLoginDto = checkSuccess(userDo);
        return userLoginDto;
    }

    private UserLoginDto checkSuccess(UserDO userDo) {
        UserLoginDto userLoginDto = new UserLoginDto();
        Map<java.lang.String,Object> map = new HashMap<>();
        map.put("userId", userDo.getId());
        map.put("identity", userDo.getIdentity());
        java.lang.String token = JWTUtil.genJwt(map);
        userLoginDto.setToken(token);
        userLoginDto.setIdentity(IdentityEnum.forName(userDo.getIdentity()));

        return userLoginDto;
    }


    private void checkRegisterParam(UserRegisterParam param) {
        /**
         1. 参数为空
         */
        if (param == null) {
            logger.error("服务层的{register}参数为空");
            throw new ServiceException(ServiceErrorCodeConstant.REGISTER_PARAM_EMPTY);

        }

        /**
          2. 校验手机号的格式
         */
        if (!RegexUtil.checkMobile(param.getPhoneNumber())) {
            logger.error("服务层的{register}手机号格式错误: " + param.getPhoneNumber());
            throw  new ServiceException(ServiceErrorCodeConstant.REGISTER_PHONE_FORMAT_ERROR);
        }


        /**
         * 3. 校验手机号是否已经存在数据库中了
         */
        if (isExistPhone(param.getPhoneNumber())) {
            logger.error("服务层的{register}手机号已经被使用: " + param.getPhoneNumber());
            throw new ServiceException(ServiceErrorCodeConstant.REGISTER_PHONE_EXIST_ERROR);
        }


        /**
          4. 校验邮箱格式
         */
        if (!StringUtils.hasLength(param.getEmail()) || !RegexUtil.checkMail(param.getEmail())) {
            logger.error("服务层的{register}邮箱格式不正确: " + param.getEmail());
            throw new ServiceException(ServiceErrorCodeConstant.REGISTER_MAIL_FORMAT_ERROR);
        }

        /**
         5。 校验邮箱是否存在数据库中
         */
        if (isExistMail(param.getEmail())) {
            logger.error("服务层的{register}邮箱已存在: " + param.getEmail());
            throw new ServiceException(ServiceErrorCodeConstant.REGISTER_MAIL_EXIST);
        }

        /**
          * 6. 校验身份信息
         */
        if (param.getIdentity()!= null && null == IdentityEnum.forName(param.getIdentity())) {
            logger.error("服务层的{register}身份信息错误: " + param.getIdentity());
            throw new ServiceException(ServiceErrorCodeConstant.REGISTER_IDENTITY_ERROR);
        }

        /**
          7. 校验管理员的密码是否存在
         */
        if (IdentityEnum.ADMIN.name().equalsIgnoreCase(param.getIdentity()) && param.getPassword() == null) {
            logger.error("服务层的{register}管理员的密码不存在:" + param.getPassword() );
            throw new ServiceException(ServiceErrorCodeConstant.REGISTER_ADMIN_PASSWORD_NO_EXIST);
        }

        /**
          8. 校验密码是否是六位以上的
         */
        if (!param.getIdentity().equalsIgnoreCase("NORMAL") && !RegexUtil.checkPassword(param.getPassword())) {
            logger.error("服务层的{register}密码少于六位或高于12位：" + param.getPassword());
            throw new ServiceException(ServiceErrorCodeConstant.REGISTER_PASSWORD_FORMAT_ERROR);
        }

    }

    /**
     *
     * 检查邮箱是否被使用
     *
     * @param email
     * @return 存在就返回 true
     */

    private boolean isExistMail(java.lang.String email) {
        int count = userMapper.selectCountByMail(email);
        return count > 0;
    }

    /**
     * 检查电话号码是否被使用
     *
     * @param phoneNumber
     * @return 如果存在就返回 true
     */
    private boolean isExistPhone(java.lang.String phoneNumber) {

        int count = userMapper.selectCountByPhone(new EncryptDO(phoneNumber));

        return count > 0;
    }



}
